package com.ivo.act.controller;

import com.ivo.act.common.result.Result;
import com.ivo.act.common.utils.ResultUtil;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 流程图管理
 * @author wj
 * @version 1.0
 */
@Controller
@RequestMapping("/process")
public class ProcessController {

    @Resource
    private DataSource dataSource;

    @Resource
    private RuntimeService runtimeService;

    @Resource
    private RepositoryService repositoryService;

    @Resource
    private HistoryService historyService;

    /**
     * 获取签核单的流程图
     * @param orderNumber 单号
     * @param sid SID
     */
    @RequestMapping("/diagram")
    public void diagram(String orderNumber, @RequestParam(defaultValue = "") String sid,
                        HttpServletResponse response) {
        if(StringUtils.isEmpty(sid)) sid = orderNumber.substring(0, orderNumber.length() - 9);
        //获取流程定义ID
        String procDefId = getProcessDef(orderNumber, sid);
        if(StringUtils.isEmpty(procDefId)) return;

        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(procDefId).singleResult();
        if(processDefinition == null) return;

        Deployment deployment = repositoryService.createDeploymentQuery()
                .deploymentId(processDefinition.getDeploymentId())
                .singleResult();

        InputStream in = repositoryService.getResourceAsStream(deployment.getId(), processDefinition.getResourceName());

        // 输出流程图
        byte[] b = new byte[2048];
        int len;
        try {
            ServletOutputStream outputStream = response.getOutputStream();
            while ((len = in.read(b, 0, b.length)) != -1) {
                outputStream.write(b, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取流程的签核历史
     * @param orderNumber 单号
     * @return Result
     */
    @RequestMapping("/history")
    @ResponseBody
    public Result historicActivityInstance(String orderNumber) {
        String procInst = getProcessInst(orderNumber);
        if(StringUtils.isEmpty(procInst)) return ResultUtil.success();

        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(procInst).list().get(0);
        if(processInstance == null) return ResultUtil.success();

        // 获取流程定义信息
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());

        //获取流程历史中已执行节点
        List<HistoricActivityInstance> historyList = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(procInst)
                .orderByHistoricActivityInstanceId().asc().list();

        // 高亮已执行环节id集合
        List<String> highLightedActivitis = new ArrayList<>();
        for (HistoricActivityInstance tempActivity : historyList) {
            String activityId = tempActivity.getActivityId();
            highLightedActivitis.add(activityId);
        }

        // 高亮线路id集合
        List<String> highLightedFlows = getHighLightedFlows(bpmnModel, historyList);

        //高亮当前节点
        List<String> highLightedCurrentNode = new ArrayList<>();
        Set<String> currIds = runtimeService.createExecutionQuery().processInstanceId(procInst).list()
                .stream().map(Execution::getActivityId).collect(Collectors.toSet());
        for(String currId : currIds) {
            if(StringUtils.isNotEmpty(currId)) highLightedCurrentNode.add(currId);
        }

        Map<String, List<String>> map = new HashMap<>();
        map.put("highLightedActivitis", highLightedActivitis);
        map.put("highLightedFlows", highLightedFlows);
        map.put("highLightedCurrentNode", highLightedCurrentNode);
        return ResultUtil.success(map);
    }

    public List<String> getHighLightedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances) {
        // 24小时制
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // 用以保存高亮的线flowId
        List<String> highFlows = new ArrayList<String>();

        for (int i = 0; i < historicActivityInstances.size() - 1; i++) {
            // 对历史流程节点进行遍历
            // 得到节点定义的详细信息
            FlowNode activityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(i).getActivityId());
            // 用以保存后续开始时间相同的节点
            List<FlowNode> sameStartTimeNodes = new ArrayList<FlowNode>();
            FlowNode sameActivityImpl1 = null;
            // 第一个节点
            HistoricActivityInstance activityImpl_ = historicActivityInstances.get(i);
            HistoricActivityInstance activityImp2_;

            for (int k = i + 1; k <= historicActivityInstances.size() - 1; k++) {
                // 后续第1个节点
                activityImp2_ = historicActivityInstances.get(k);

                // 都是usertask，且主节点与后续节点的开始时间相同，说明不是真实的后继节点
                if (!(
                        activityImpl_.getActivityType().equals("userTask")
                        && activityImp2_.getActivityType().equals("userTask")
                        && df.format(activityImpl_.getStartTime()).equals(df.format(activityImp2_.getStartTime()))
                )) {
                    // 找到紧跟在后面的一个节点
                    sameActivityImpl1 = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(k).getActivityId());
                    break;
                }
            }
            // 将后面第一个节点放在时间相同节点的集合里
            sameStartTimeNodes.add(sameActivityImpl1);
            for (int j = i + 1; j < historicActivityInstances.size() - 1; j++) {
                // 后续第一个节点
                HistoricActivityInstance activityImpl1 = historicActivityInstances.get(j);
                // 后续第二个节点
                HistoricActivityInstance activityImpl2 = historicActivityInstances.get(j + 1);

                // 如果第一个节点和第二个节点开始时间相同保存
                if (df.format(activityImpl1.getStartTime()).equals(df.format(activityImpl2.getStartTime()))) {
                    FlowNode sameActivityImpl2 = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityImpl2.getActivityId());
                    sameStartTimeNodes.add(sameActivityImpl2);
                } else {
                    // 有不相同跳出循环
                    break;
                }
            }
            // 取出节点的所有出去的线
            List<SequenceFlow> pvmTransitions = activityImpl.getOutgoingFlows();

            // 对所有的线进行遍历
            for (SequenceFlow pvmTransition : pvmTransitions) {
                // 如果取出的线的目标节点存在时间相同的节点里，保存该线的id，进行高亮显示
                FlowNode pvmActivityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(pvmTransition.getTargetRef());
                if (sameStartTimeNodes.contains(pvmActivityImpl)) {
                    highFlows.add(pvmTransition.getId());
                }
            }
        }
        return highFlows;
    }



    /**
     * 获取单号对应的流程定义ID
     * @param orderNumber 单号
     * @param sid SID
     * @return PROC_DEF_ID
     */
    public String getProcessDef(String orderNumber, String sid) {
        String procDefId = "";
        //当签核单未签完时，从act_ex_execution的task查询得到PROC_DEF_ID_
        String sql1 = "SELECT PROC_DEF_ID_ FROM act_ru_task where id_ in (\n" +
                "select TASK from act_ex_execution where order_number='"+orderNumber+"' \n" +
                ") limit 0,1";

        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            conn = dataSource.getConnection();
            st = conn.createStatement();
            rs = st.executeQuery(sql1);
            while (rs.next()) {
                procDefId = rs.getString("PROC_DEF_ID_");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                assert rs != null;
                rs.close();
                st.close();
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if(StringUtils.isNotEmpty(procDefId)) return procDefId;

        //当签核单签完时，从单子的起草时间判断版本最近的部署时间，获取流程定义ID
        String sql2 = "SELECT A.ID_ FROM \n" +
                "ACT_RE_PROCDEF A\n" +
                "LEFT JOIN act_re_deployment B ON A.DEPLOYMENT_ID_=B.ID_\n" +
                "where KEY_='"+sid+"' AND DEPLOY_TIME_<(\n" +
                "select DRAFT_DATE from act_ex_history where NODE='S020' AND order_number = '"+orderNumber+"' limit 0,1\n" +
                ") ORDER BY DEPLOY_TIME_ DESC limit 0,1";

        try {
            conn = dataSource.getConnection();
            st = conn.createStatement();
            rs = st.executeQuery(sql2);
            while (rs.next()) {
                procDefId = rs.getString("ID_");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                assert rs != null;
                rs.close();
                st.close();
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        return procDefId;
    }

    /**
     * 获取签核单的流程实例ID
     * @param orderNumber 单号
     * @return PROC_INST_ID
     */
    public String getProcessInst(String orderNumber) {
        String processInst = "";
        String sql1 = "SELECT PROC_INST_ID_ FROM act_ru_task where id_ in (\n" +
                "select TASK from act_ex_execution where order_number='"+orderNumber+"' \n" +
                ") limit 0,1";
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            conn = dataSource.getConnection();
            st = conn.createStatement();
            rs = st.executeQuery(sql1);
            while (rs.next()) {
                processInst = rs.getString("PROC_INST_ID_");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                assert rs != null;
                rs.close();
                st.close();
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        return processInst;
    }

}
